﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Lacrima.Framework.Infrastructure
{
    public static class ReflectionUtil
    {
        /// <summary>
        /// 此列表中的属性名称不需要进行解析
        /// </summary>
        public static string[] ignoreProperties = new string[]{ "metaDataProperty"
                                                                                        };

        /// <summary>
        /// 内存对象主干属性名，不是基本类型并且所属类型不在typeList类型内，
        /// 但是需要直接解析到实体类属性中，例如Object类（因为如果typeList包含Object类的话下面的子节点条件分支就不会走了）
        /// </summary>
        public static IList<string> specialPropertyList = new List<string>
        {   
            "Begin_or_Position","End_or_Position"
        };

        public static string[] defaultValueProperties = new string[] { "Value",
                                                                                                      "remoteSchema"
                                                                                                    };

        public static string[] validProperties = new string[] { "Value",
                                                                                        "remoteSchema",
                                                                                        "uom"
                                                                                    };

        private static IDictionary<Type, Type> _specialTypeDictionary { get; set; }

        public static IDictionary<Type, Type> specialTypeDictionary
        {
            get
            {
                if (_specialTypeDictionary == null)
                {
                    _specialTypeDictionary = new Dictionary<Type, Type>();
                    //_specialTypeDictionary.Add(new KeyValuePair<Type, Type>(typeof(TimePrimitivePropertyType), typeof(AbstractTimePrimitiveType)));
                    //_specialTypeDictionary.Add(new KeyValuePair<Type, Type>(typeof(ElevatedPointPropertyType), typeof(ElevatedPointType)));
                }
                return _specialTypeDictionary;
            }
        }

        private static IDictionary<string, Func<PropertyInfo, bool>> _trunkSpecialPropertyDictionary { get; set; }

        /// <summary>
        /// 内存对象中的主干属性上的特殊字段
        /// </summary>
        public static IDictionary<string, Func<PropertyInfo, bool>> trunkSpecialPropertyDictionary
        {
            get
            {
                if (_trunkSpecialPropertyDictionary == null)
                {
                    _trunkSpecialPropertyDictionary = new Dictionary<string, Func<PropertyInfo, bool>>();
                    _trunkSpecialPropertyDictionary.Add(new KeyValuePair<string, Func<PropertyInfo, bool>>("longitude", o => o.Name.Equals("Item", StringComparison.OrdinalIgnoreCase)));
                    _trunkSpecialPropertyDictionary.Add(new KeyValuePair<string, Func<PropertyInfo, bool>>("latitude", o => o.Name.Equals("Item", StringComparison.OrdinalIgnoreCase)));
                }
                return _trunkSpecialPropertyDictionary;
            }
        }
            
        /// <summary>
        /// 解析属性值到目标对象
        /// </summary>
        /// <typeparam name="TSource">解析源对象的类型</typeparam>
        /// <typeparam name="TResult">解析目标对象的类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="result">目标对象</param>
        /// <param name="predicate">源对象属性选择表达式</param>
        public static void TryParsePropertyValue<TSource, TResult>(this TSource source, out TResult result, Func<PropertyInfo, bool> predicate = null)
            where TSource : class
        {
            PropertyInfo property = typeof(TSource).GetProperties().Where(predicate).First();
            object sourceValue = property.GetValue(source);
            if (sourceValue == null)
            {
                result = default(TResult);
            }
            else
            {
                result = (TResult)Convert.ChangeType(sourceValue, typeof(TResult));
            }
        }

        /// <summary>
        /// 设置对象属性值
        /// </summary>
        /// <typeparam name="TSource">解析源对象的类型</typeparam>
        /// <typeparam name="TResult">解析目标对象的类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="result">目标对象</param>
        /// <param name="sourceProperty">源类型属性列表</param>
        /// <param name="resultProperty">目标类型属性列表</param>
        public static void SetValueByProperty<TSource, TResult>(this TSource source, PropertyInfo sourceProperty, TResult result, PropertyInfo resultProperty)
            where TSource : class
        {
            object sourceValue = source.GetValueByProperty(sourceProperty);
            if (sourceValue != null)
            {
                // 适用于内存对象的一个对象包含实体对象两个字段的情况
                if (sourceProperty.Name.Equals("uom", StringComparison.OrdinalIgnoreCase))
                {
                    PropertyInfo property = result.GetType().GetProperties()
                                                                   .Single(o => o.Name.Equals(string.Format("{0}_{1}", resultProperty.Name, sourceProperty.Name), StringComparison.OrdinalIgnoreCase));
                    property.SetValue(result, Convert.ChangeType(sourceValue, property.PropertyType));
                }
                //else if (resultProperty.Name.Equals("longitude"))
                //{
                //    resultProperty.SetValue(result, (sourceProperty.GetValue(source) as DirectPositionType).Text.FirstOrDefault());
                //}
                //else if (resultProperty.Name.Equals("latitude"))
                //{
                //    resultProperty.SetValue(result, (sourceProperty.GetValue(source) as DirectPositionType).Text.LastOrDefault());
                //}
                else
                {
                    resultProperty.SetValue(result, Convert.ChangeType(sourceValue, resultProperty.PropertyType));
                }
            }
        }

        /// <summary>
        /// 设置对象属性值
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TResult">目标类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="result">目标对象</param>
        /// <param name="sourceProperty">源类型属性列表</param>
        /// <param name="predicate">目标属性筛选规则</param>
        public static void SetValueByProperty<TSource, TResult>(this TSource source, PropertyInfo sourceProperty, Func<PropertyInfo, bool> sourcepredicate,
                                                                                      TResult result, PropertyInfo resultProperty, Func<PropertyInfo, bool> resultPredicate)
            where TSource : class
        {
            PropertyInfo srcProperty = sourceProperty ?? source.GetType().GetProperties().Single(sourcepredicate);
            PropertyInfo resProperty = resultProperty ?? result.GetType().GetProperties().Single(resultPredicate);
            source.SetValueByProperty(srcProperty, result, resProperty);
        }

        /// <summary>
        /// 设置对象属性值
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TResult">目标类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="result">目标对象</param>
        /// <param name="predicate">目标属性筛选规则</param>
        public static void SetValueByProperty<TSource, TResult>(this TSource sourceValue, TResult result, PropertyInfo resultProperty, Func<PropertyInfo, bool> resultPredicate)
            where TSource : class
            where TResult : class
        {
            PropertyInfo resProperty = resultProperty ?? result.GetType().GetProperties().Single(resultPredicate);
            resProperty.SetValue(result, Convert.ChangeType(sourceValue, resProperty.PropertyType));
        }

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="sourceProperty">源对象在类中所属的属性信息</param>
        /// <param name="predicate">取值筛选的表达式，可以自定义生成方式</param>
        /// <returns>返回属性值</returns>
        public static object GetValueByProperty<TSource>(this TSource source, PropertyInfo sourceProperty, Func<object, object, object> predicate = null, bool isExecuteFunc = true)
                    where TSource : class
        {
            // 不解析数组类型
            bool resloveArray = false;
            if (sourceProperty == null || source == null)
            {
                return null;
            }
            else if (resloveArray && isExecuteFunc && typeof(Array).IsAssignableFrom(sourceProperty.PropertyType))
            {
                // 解析数组类型对象的值
                Array array = (source as Array);
                IEnumerator enumerator = (source as Array).GetEnumerator();
                enumerator.MoveNext();
                var current = enumerator.Current;

                if (predicate == null)
                {
                    StringBuilder buffer = new StringBuilder();
                    buffer.Append(current.GetDefaultValue());
                    while (enumerator.MoveNext())
                    {
                        buffer.Append(",");
                        buffer.Append(enumerator.Current.GetDefaultValue());
                    }
                    return buffer.ToString();
                }
                else
                {
                    var buffer = current.GetDefaultValue();
                    while (enumerator.MoveNext())
                    {
                        buffer = predicate(buffer, enumerator.Current);
                    }
                    return buffer;
                }
            }
            else
                return sourceProperty.GetValue(source);
        }

        /// <summary>
        /// 通过函数表达式获取
        /// </summary>
        /// <typeparam name="TSource">解析源类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="predicate">属性选择条件表达式</param>
        /// <param name="function">复杂逻辑表达式</param>
        /// <returns>源对象中符合表达式条件的属性值</returns>
        public static object GetValueByProperty<TSource>(this TSource source, Func<PropertyInfo, bool> predicate, Func<object, object, object> function = null, bool isExecuteFunc = true)
            where TSource : class
        {
            if (predicate != null)
            {
                // 这里采用SingleOrDefault方法确保源对象中只有一个符合条件的属性
                PropertyInfo sourceProperty = source.GetType().GetProperties().SingleOrDefault(predicate);
                if (sourceProperty != null)
                {
                    return source.GetValueByProperty(sourceProperty, function, isExecuteFunc);
                }
                else
                    return sourceProperty;
            }
            return source.GetDefaultValue();
        }

        /// <summary>
        /// 获取目标对象中的对应属性列表中的值
        /// </summary>
        /// <typeparam name="TSource">解析源类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="sourceProperty">属性数组</param>
        /// <returns>返回值数组</returns>
        public static object[] GetValuesByProperty<TSource>(this TSource source, PropertyInfo[] sourceProperty)
            where TSource : class
        {
            return sourceProperty.Select(o => source.GetValueByProperty(o)).ToArray();
        }

        /// <summary>
        /// 根据条件表达式获取目标对象中的对应属性列表中的值
        /// </summary>
        /// <typeparam name="TSource">解析源类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="predicate">属性数组筛选表达式</param>
        /// <returns>返回值数组</returns>
        public static object[] GetValuesByProperty<TSource>(this TSource source, Func<PropertyInfo, bool> predicate)
            where TSource : class
        {
            PropertyInfo[] sourceProperty = source.GetType().GetProperties().Where(predicate).ToArray();
            if (predicate != null)
            {
                return sourceProperty ?? source.GetValuesByProperty(sourceProperty);
            }
            return null;
        }

        /// <summary>
        /// 根据属性筛选表达式获取对象的默认值
        /// </summary>
        /// <typeparam name="TSource">解析源类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="predicate">属性筛选表达式</param>
        /// <returns>返回源对象中指定属性默认值</returns>
        public static object GetDefaultValue<TSource>(this TSource source, Func<PropertyInfo, bool> predicate = null)
            where TSource : class
        {
            Type sourceType = source.GetType();
            PropertyInfo sourceDefaultPreperty = source.GetDefaultProperty(predicate);

            if (sourceDefaultPreperty != null)
            {
                return source.GetValueByProperty(sourceDefaultPreperty);
            }
            else if (typeof(string).IsAssignableFrom(sourceType) || sourceType.IsValueType)
            {
                return source;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 根据属性筛选表达式获取对象的默认属性
        /// </summary>
        /// <typeparam name="TSource">解析源类型</typeparam>
        /// <param name="source">解析源对象</param>
        /// <param name="predicate">属性筛选表达式</param>
        /// <returns>返回源对象中指定属性默认值</returns>
        public static PropertyInfo GetDefaultProperty<TSource>(this TSource source, Func<PropertyInfo, bool> predicate = null)
            where TSource : class
        {
            Type sourceType = source.GetType();
            PropertyInfo[] sourceProperties = sourceType.GetProperties();
            PropertyInfo sourceDefaultPreperty;
            if (predicate != null)
            {
                sourceDefaultPreperty = sourceProperties.FirstOrDefault(predicate);
            }
            else
            {
                sourceDefaultPreperty = sourceProperties.FirstOrDefault(n => ReflectionUtil.defaultValueProperties.Contains(n.Name, StringComparer.OrdinalIgnoreCase));
            }
            return sourceDefaultPreperty;
        }

        /// <summary>
        /// 根据属性筛选表达式通过递归方式获取对象的默认属性
        /// </summary>
        /// <param name="sourceType">Type of the source.</param>
        /// <param name="predicate">属性筛选表达式</param>
        /// <param name="depth">最大递归深度</param>
        /// <returns>返回源对象中指定属性</returns>
        public static PropertyInfo GetRecursiveProperty(this Type sourceType, Func<PropertyInfo, bool> predicate, int depth = 0)
        {
            PropertyInfo[] sourceProperties = sourceType.GetProperties();
            PropertyInfo sourceDefaultPreperty = null;
            if (predicate != null && depth < 10)
            {
                sourceDefaultPreperty = sourceProperties.FirstOrDefault(predicate);
                if (sourceDefaultPreperty == null)
                {
                    Type[] subType = sourceProperties.Select(o => o.PropertyType)
                                                   .Where(o => o.IsClass && !o.IsEquivalentTo(typeof(string)) && !o.IsAbstract)
                                                   .ToArray();
                    foreach (var type in subType)
                    {
                        sourceDefaultPreperty = type.GetRecursiveProperty(predicate, depth + 1);
                        if (sourceDefaultPreperty != null)
                        {
                            return sourceDefaultPreperty;
                        }
                    }
                }
            }
            return sourceDefaultPreperty;
        }
    }
}
